package cn.wolfcode.shiro;

import cn.wolfcode.domain.Employee;
import cn.wolfcode.exception.LogicException;
import cn.wolfcode.service.IEmployeeService;
import cn.wolfcode.service.IPermissionService;
import cn.wolfcode.service.IRoleService;
import cn.wolfcode.util.UserContext;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * 自定义数据源
 */
@Component
public class CrmRealm extends AuthorizingRealm{

    @Autowired
    private IEmployeeService employeeService;

    @Autowired
    private IPermissionService permissionService;

    @Autowired
    private IRoleService roleService;

    @Autowired
    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        super.setCredentialsMatcher(credentialsMatcher);
    }

    /**
     * 提供授权信息
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("-------------------------------");
        //1.获取当前登录的员工的对象，获取员工id
        Employee currentUser = UserContext.getCurrentUser();
        Long employeeId = currentUser.getId();
        //Employee employee = (Employee) principals.getPrimaryPrincipal();

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //2.查询数据库该员工的拥有的角色和权限数据
        //3.设置当前登录用户的拥有的角色和权限数据
        if(!currentUser.isAdmin()){ //判断是否管理员，如果不是就需要查询
            List<String> expressions = permissionService.selectExpressionByEmpId(employeeId);
            info.addStringPermissions(expressions); //设置权限数据
            List<String> sns = roleService.selectSnByEmpId(employeeId);
            info.addRoles(sns); //设置角色数据
        }else{
            //权限拦截的功能是shiro来做的，不知道Employee的属性的意义，它还是按照权限表达式来判断的,可以给通配符
            info.addStringPermission("*:*");
            info.addRole("admin"); //管理员的角色
        }
        return info;
    }


    /**
     * 提供认证信息
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //当前的方法如果直接返回null，shiro会自动抛出账号不存在的异常
        //如果返回的结果不为null，shiro会自动从返回的对象中获取到真实的密码，再与token去对比

        //1.获取令牌中的用户名(前端传来的)
        String username = (String)token.getPrincipal();
        Object credentials = token.getCredentials();
      /*  UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
        usernamePasswordToken.getUsername()*/
        //2.查询数据库中是否存在该员工
        Employee employee = employeeService.selectByName(username);
        //3.如果不存在，直接返回null
        if(employee!=null){
            /*   if(!false){  如果直接抛shiro提供的异常，就不会被封装，如果抛自定义的异常，就会被shiro封装
                throw new DisabledAccountException("账号被禁用");
            }*/
            //4.如果存在，返回SimpleAuthenticationInfo对象
            //身份信息可以在任意地方获取到，用来跟subject绑定在一起的
            //在项目中时就直接传入员工对象，跟subject绑定在一起，方便我们后续在任意地方获取当前登录的员工对象
            //传入真实的密码，shiro会自动判断是否正确
            //传入当前数据源的名字，对于我们现在的项目没有作用
            //一般是一个项目中有多个数据源时，需要做标志，代表数据是从哪个数据源中查的
//            return new SimpleAuthenticationInfo(employee,employee.getPassword(),this.getName());

            //第三个参数可以指定加密时的盐
            return new SimpleAuthenticationInfo(employee,employee.getPassword(), ByteSource.Util.bytes(username),this.getName());
        }
        return null;
    }
}
